﻿#include "PhotoShop_Algorithm.h"

using namespace cv;


void PhotoShop::measureTime(const std::function<void()>& func, const std::string& description)
{
	auto start = std::chrono::high_resolution_clock::now();
	func();
	auto end = std::chrono::high_resolution_clock::now();
	std::chrono::duration<double> duration = end - start;
	std::cout << description << ": " << duration.count() << " seconds" << std::endl;
}


cv::Mat PhotoShop::ColorTrans(Mat src, int op) {
	//op=1 呈现碧绿效果，就是要使色彩呈暗绿色，给人诡异的感觉
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC3);
	if (op == 1) {
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++) {
				int b = src.at<Vec3b>(i, j)[0];
				int g = src.at<Vec3b>(i, j)[1];
				int r = src.at<Vec3b>(i, j)[2];
				int R = (g - b) * (g - b) / 128;
				int G = (r - b) * (r - b) / 128;
				int B = (r - g) * (r - g) / 128;
				if (R > 255) R = 255;
				if (R < 0) R = 0;
				if (G > 255) G = 255;
				if (G < 0) G = 0;
				if (B > 255) B = 255;
				if (B < 0) B = 0;
				dst.at<Vec3b>(i, j)[0] = B;
				dst.at<Vec3b>(i, j)[1] = G;
				dst.at<Vec3b>(i, j)[2] = R;
			}
		}
	}
	else if (op == 2) {
		//op=2 棕褐色效果就是要实现那种图像模糊、略带发黄的老照片的感觉，别具风情
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++) {
				int b = src.at<Vec3b>(i, j)[0];
				int g = src.at<Vec3b>(i, j)[1];
				int r = src.at<Vec3b>(i, j)[2];
				int R = 0.393 * r + 0.769 * g + 0.189 * b;
				int G = 0.349 * r + 0.686 * g + 0.168 * b;
				int B = 0.272 * r + 0.534 * g + 0.131 * b;
				if (R > 255) R = 255;
				if (R < 0) R = 0;
				if (G > 255) G = 255;
				if (G < 0) G = 0;
				if (B > 255) B = 255;
				if (B < 0) B = 0;
				dst.at<Vec3b>(i, j)[0] = B;
				dst.at<Vec3b>(i, j)[1] = G;
				dst.at<Vec3b>(i, j)[2] = R;
			}
		}
	}
	else if (op == 3) {
		//op=3 冰冻效果就是使图像呈现一种晶莹的淡蓝色
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++) {
				int b = src.at<Vec3b>(i, j)[0];
				int g = src.at<Vec3b>(i, j)[1];
				int r = src.at<Vec3b>(i, j)[2];
				int R = abs(r - g - b) * 3 / 2;
				int G = abs(g - b - r) * 3 / 2;
				int B = abs(b - r - g) * 3 / 2;
				if (R > 255) R = 255;
				if (R < 0) R = 0;
				if (G > 255) G = 255;
				if (G < 0) G = 0;
				if (B > 255) B = 255;
				if (B < 0) B = 0;
				dst.at<Vec3b>(i, j)[0] = B;
				dst.at<Vec3b>(i, j)[1] = G;
				dst.at<Vec3b>(i, j)[2] = R;
			}
		}
	}
	else if (op == 4) {
		//op=4 熔铸效果类似打铁的场景
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++) {
				int b = src.at<Vec3b>(i, j)[0];
				int g = src.at<Vec3b>(i, j)[1];
				int r = src.at<Vec3b>(i, j)[2];
				int R = r * 128 / (g + b + 1);
				int G = g * 128 / (b + r + 1);
				int B = b * 128 / (r + g + 1);
				if (R > 255) R = 255;
				if (R < 0) R = 0;
				if (G > 255) G = 255;
				if (G < 0) G = 0;
				if (B > 255) B = 255;
				if (B < 0) B = 0;
				dst.at<Vec3b>(i, j)[0] = B;
				dst.at<Vec3b>(i, j)[1] = G;
				dst.at<Vec3b>(i, j)[2] = R;
			}
		}
	}
	else if (op == 5) {
		//op=5 暗调效果是通过降低色彩的各个分量，使整幅图像变得深谙
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++) {
				int b = src.at<Vec3b>(i, j)[0];
				int g = src.at<Vec3b>(i, j)[1];
				int r = src.at<Vec3b>(i, j)[2];
				int R = r * r / 255;
				int G = g * g / 255;
				int B = b * b / 255;
				/*if (R > 255) R = 255;
				if (R < 0) R = 0;
				if (G > 255) G = 255;
				if (G < 0) G = 0;
				if (B > 255) B = 255;
				if (B < 0) B = 0;*/
				dst.at<Vec3b>(i, j)[0] = B;
				dst.at<Vec3b>(i, j)[1] = G;
				dst.at<Vec3b>(i, j)[2] = R;
			}
		}
	}
	else if (op == 6) {
		//op=6 对调效果通过对彩色分量进行轮换的方式重新组合得到新的色彩
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++) {
				int b = src.at<Vec3b>(i, j)[0];
				int g = src.at<Vec3b>(i, j)[1];
				int r = src.at<Vec3b>(i, j)[2];
				int R = g * b / 255;
				int G = b * r / 255;
				int B = r * g / 255;
				/*if (R > 255) R = 255;
				if (R < 0) R = 0;
				if (G > 255) G = 255;
				if (G < 0) G = 0;
				if (B > 255) B = 255;
				if (B < 0) B = 0;*/
				dst.at<Vec3b>(i, j)[0] = B;
				dst.at<Vec3b>(i, j)[1] = G;
				dst.at<Vec3b>(i, j)[2] = R;
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::ColorTransPtr(const cv::Mat& src, int op)
{
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC3);

	if (op == 1) {
		for (int i = 0; i < row; i++) {
			const Vec3b* srcPtr = src.ptr<Vec3b>(i);
			Vec3b* dstPtr = dst.ptr<Vec3b>(i);
			for (int j = 0; j < col; j++) {
				int b = srcPtr[j][0];
				int g = srcPtr[j][1];
				int r = srcPtr[j][2];
				int R = (g - b) * (g - b) / 128;
				int G = (r - b) * (r - b) / 128;
				int B = (r - g) * (r - g) / 128;
				dstPtr[j][0] = COLOR_0_255(B);
				dstPtr[j][1] = COLOR_0_255(G);
				dstPtr[j][2] = COLOR_0_255(R);
			}
		}
	}
	else if (op == 2) {
		for (int i = 0; i < row; i++) {
			const Vec3b* srcPtr = src.ptr<Vec3b>(i);
			Vec3b* dstPtr = dst.ptr<Vec3b>(i);
			for (int j = 0; j < col; j++) {
				int b = srcPtr[j][0];
				int g = srcPtr[j][1];
				int r = srcPtr[j][2];
				int R = 0.393 * r + 0.769 * g + 0.189 * b;
				int G = 0.349 * r + 0.686 * g + 0.168 * b;
				int B = 0.272 * r + 0.534 * g + 0.131 * b;
				dstPtr[j][0] = COLOR_0_255(B);
				dstPtr[j][1] = COLOR_0_255(G);
				dstPtr[j][2] = COLOR_0_255(R);
			}
		}
	}
	else if (op == 3) {
		for (int i = 0; i < row; i++) {
			const Vec3b* srcPtr = src.ptr<Vec3b>(i);
			Vec3b* dstPtr = dst.ptr<Vec3b>(i);
			for (int j = 0; j < col; j++) {
				int b = srcPtr[j][0];
				int g = srcPtr[j][1];
				int r = srcPtr[j][2];
				int R = abs(r - g - b) * 3 / 2;
				int G = abs(g - b - r) * 3 / 2;
				int B = abs(b - r - g) * 3 / 2;
				dstPtr[j][0] = COLOR_0_255(B);
				dstPtr[j][1] = COLOR_0_255(G);
				dstPtr[j][2] = COLOR_0_255(R);
			}
		}
	}
	else if (op == 4) {
		for (int i = 0; i < row; i++) {
			const Vec3b* srcPtr = src.ptr<Vec3b>(i);
			Vec3b* dstPtr = dst.ptr<Vec3b>(i);
			for (int j = 0; j < col; j++) {
				int b = srcPtr[j][0];
				int g = srcPtr[j][1];
				int r = srcPtr[j][2];
				int R = r * 128 / (g + b + 1);
				int G = g * 128 / (b + r + 1);
				int B = b * 128 / (r + g + 1);
				dstPtr[j][0] = COLOR_0_255(B);
				dstPtr[j][1] = COLOR_0_255(G);
				dstPtr[j][2] = COLOR_0_255(R);
			}
		}
	}
	else if (op == 5) {
		for (int i = 0; i < row; i++) {
			const Vec3b* srcPtr = src.ptr<Vec3b>(i);
			Vec3b* dstPtr = dst.ptr<Vec3b>(i);
			for (int j = 0; j < col; j++) {
				int b = srcPtr[j][0];
				int g = srcPtr[j][1];
				int r = srcPtr[j][2];
				int R = r * r / 255;
				int G = g * g / 255;
				int B = b * b / 255;
				dstPtr[j][0] = B;
				dstPtr[j][1] = G;
				dstPtr[j][2] = R;
			}
		}
	}
	else if (op == 6) {
		for (int i = 0; i < row; i++) {
			const Vec3b* srcPtr = src.ptr<Vec3b>(i);
			Vec3b* dstPtr = dst.ptr<Vec3b>(i);
			for (int j = 0; j < col; j++) {
				int b = srcPtr[j][0];
				int g = srcPtr[j][1];
				int r = srcPtr[j][2];
				int R = g * b / 255;
				int G = b * r / 255;
				int B = r * g / 255;
				dstPtr[j][0] = B;
				dstPtr[j][1] = G;
				dstPtr[j][2] = R;
			}
		}
	}

	return dst;
}


cv::Mat PhotoShop::MinMax(Mat src1, Mat src2, int flag) {
	int row = src1.rows;
	int col = src1.cols;
	if (row != src2.rows || col != src2.cols) {
		fprintf_s(stderr, "Input shape don't match!");
		exit(-1);
	}
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				if (flag == 0) {
					dst.at<Vec3b>(i, j)[k] = max(src1.at<Vec3b>(i, j)[k], src2.at<Vec3b>(i, j)[k]);
				}
				else {
					dst.at<Vec3b>(i, j)[k] = min(src1.at<Vec3b>(i, j)[k], src2.at<Vec3b>(i, j)[k]);
				}
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::MinMaxPtr(const cv::Mat& src1, const cv::Mat& src2, int flag)
{
	int row = src1.rows;
	int col = src1.cols;
	if (row != src2.rows || col != src2.cols || src1.channels() != src2.channels()) {
		fprintf_s(stderr, "Input shape or channels don't match!");
		exit(-1);
	}
	cv::Mat dst(row, col, src1.type());

	// 根据flag选择合适的操作函数
	auto compareFunc = (flag == 0) ? MAX_CUST : MIN_CUST;

	int channels = src1.channels();
	if (channels == 1) {
		for (int i = 0; i < row; i++) {
			const uchar* src1Ptr = src1.ptr<uchar>(i);
			const uchar* src2Ptr = src2.ptr<uchar>(i);
			uchar* dstPtr = dst.ptr<uchar>(i);
			for (int j = 0; j < col; j++) {
				*dstPtr++ = compareFunc(*src1Ptr++, *src2Ptr++);
			}
		}
	}
	else if (channels == 3) {
		for (int i = 0; i < row; i++) {
			//const Vec3b* src1Ptr = src1.ptr<Vec3b>(i);
			//const Vec3b* src2Ptr = src2.ptr<Vec3b>(i);
			//Vec3b* dstPtr = dst.ptr<Vec3b>(i);
			//for (int j = 0; j < col; j++) {
			//	 for (int k = 0; k < 3; k++) {
			//	 	dstPtr[j][k] = compareFunc(src1Ptr[j][k], src2Ptr[j][k]);
			//	 }
			//}

			const uchar* src1Ptr = src1.ptr<uchar>(i);
			const uchar* src2Ptr = src2.ptr<uchar>(i);
			uchar* dstPtr = dst.ptr<uchar>(i);
			for (int j = 0; j < col; j++) {
				*dstPtr++ = compareFunc(*src1Ptr++, *src2Ptr++);
				*dstPtr++ = compareFunc(*src1Ptr++, *src2Ptr++);
				*dstPtr++ = compareFunc(*src1Ptr++, *src2Ptr++);
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::DiamondEmboss(Mat src, DIR dir, int offset)
{
	int row = src.rows;
	int col = src.cols;
	int ioffset = 0;
	int joffset = 0;
	switch (dir)
	{
	case DIR::N:
		ioffset = -1;
		joffset = 0;
		break;
	case DIR::NE:
		ioffset = -1;
		joffset = 1;
		break;
	case DIR::E:
		ioffset = 0;
		joffset = 1;
		break;
	case DIR::SE:
		ioffset = 1;
		joffset = 1;
		break;
	case DIR::S:
		ioffset = 1;
		joffset = -1;
		break;
	case DIR::SW:
		ioffset = 0;
		joffset = -1;
		break;
	case DIR::W:
		ioffset = 0;
		joffset = -1;
		break;
	case DIR::NW:
		ioffset = 1;
		joffset = 1;
		break;
	default:
		break;
	}
	cv::Mat dst(row, col, CV_8UC3);
	int border = 1;
	for (int i = border; i < row - border; i++) {
		for (int j = border; j < col - border; j++) {
			for (int k = 0; k < 3; k++) {
				int sum = src.at<Vec3b>(i, j)[k] - src.at<Vec3b>(i - ioffset, j - joffset)[k] + offset;
				if (sum < 0) sum = -sum;
				if (sum < 64) sum = 64;
				if (sum > 255) sum = 255;
				dst.at<Vec3b>(i, j)[k] = sum;
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::DiamondEmbossPtr(const Mat& src, DIR dir, int offset) 
{
	if (src.channels() != 3) {
		fprintf_s(stderr, "Input channels must 3!");
		exit(-1);
	}
	int row = src.rows;
	int col = src.cols;
	int ioffset = 0;
	int joffset = 0;
	switch (dir) {
	case DIR::N:
		ioffset = -1;
		joffset = 0;
		break;
	case DIR::NE:
		ioffset = -1;
		joffset = 1;
		break;
	case DIR::E:
		ioffset = 0;
		joffset = 1;
		break;
	case DIR::SE:
		ioffset = 1;
		joffset = 1;
		break;
	case DIR::S:
		ioffset = 1;
		joffset = 0;
		break;
	case DIR::SW:
		ioffset = 1;
		joffset = -1;
		break;
	case DIR::W:
		ioffset = 0;
		joffset = -1;
		break;
	case DIR::NW:
		ioffset = -1;
		joffset = -1;
		break;
	default:
		break;
	}
	cv::Mat dst(row, col, CV_8UC3);
	int border = 1;
	for (int i = border; i < row - border; i++) {
		const Vec3b* srcPtr = src.ptr<Vec3b>(i);
		Vec3b* dstPtr = dst.ptr<Vec3b>(i);
		const Vec3b* srcOffsetPtr = src.ptr<Vec3b>(i - ioffset);
		for (int j = border; j < col - border; j++) {
			for (int k = 0; k < 3; k++) {
				int sum = srcPtr[j][k] - srcOffsetPtr[j - joffset][k] + offset;
				if (sum < 0) sum = -sum;
				if (sum < 64) sum = 64;
				if (sum > 255) sum = 255;
				dstPtr[j][k] = sum;
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::GaussDistributionMask(Mat src) 
{
	int row = src.rows;
	int col = src.cols;
	int center_x = row / 2;
	int center_y = col / 2;
	//获得二维高斯分布蒙版
	int R = sqrt(center_x * center_x + center_y * center_y);
	cv::Mat Gauss_map(row, col, CV_32FC1);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			float dis = sqrt(1.0 * (i - center_x) * (i - center_x) + 1.0 * (j - center_y) * (j - center_y));
			Gauss_map.at<float>(i, j) = exp(-0.5 * dis / R);
		}
	}
	//和原图相乘得到渐变映射的效果
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				dst.at<Vec3b>(i, j)[k] = src.at<Vec3b>(i, j)[k] * Gauss_map.at<float>(i, j);
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::GaussDistributionMaskPtr(const cv::Mat& src)
{
	if (src.channels() != 3) {
		fprintf_s(stderr, "Input channels must 3!");
		exit(-1);
	}
	int row = src.rows;
	int col = src.cols;
	int center_x = row / 2;
	int center_y = col / 2;
	// 获得二维高斯分布蒙版
	int R = sqrt(center_x * center_x + center_y * center_y);

	cv::Mat dst(row, col, CV_8UC3);

	for (int i = 0; i < row; i++) {
		const uchar* src_ptr = src.ptr<uchar>(i);
		uchar* dst_ptr = dst.ptr<uchar>(i);
		for (int j = 0; j < col; j++) {
			float dis = sqrt(1.0 * (i - center_x) * (i - center_x) + 1.0 * (j - center_y) * (j - center_y));
			float Gauss_value = exp(-0.5 * dis / R);
			*dst_ptr++ = *src_ptr++ * Gauss_value;
			*dst_ptr++ = *src_ptr++ * Gauss_value;
			*dst_ptr++ = *src_ptr++ * Gauss_value;
		}
	}

	return dst;

}


cv::Mat PhotoShop::Bright(cv::Mat src, int cb_)
{
	int row = src.rows;
	int col = src.cols;
	if (cb_ < -255)
		cb_ = -255;
	if (cb_ > 255)
		cb_ = 255;
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				int val = src.at<Vec3b>(i, j)[k] + cb_;
				if (val < 0) val = 0;
				else if (val > 255) val = 255;
				dst.at<Vec3b>(i, j)[k] = val;
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::BrightPtr(const cv::Mat& src, int cb_)
{
	if (src.channels() != 3) {
		fprintf_s(stderr, "Input channels must 3!");
		exit(-1);
	}
	int row = src.rows;
	int col = src.cols;
	cb_ = COLOR_255_255(cb_);
	cv::Mat dst(row, col, CV_8UC3);

	for (int i = 0; i < row; i++) {
		const uchar* src_ptr = src.ptr<uchar>(i);
		uchar* dst_ptr = dst.ptr<uchar>(i);
		for (int j = 0; j < col; j++) {
			*dst_ptr++ = static_cast<uchar>(CLAMP(*src_ptr++ + cb_));
			*dst_ptr++ = static_cast<uchar>(CLAMP(*src_ptr++ + cb_));
			*dst_ptr++ = static_cast<uchar>(CLAMP(*src_ptr++ + cb_));
		}
	}
	return dst;
}


cv::Mat PhotoShop::PaperCutArtFilter(const cv::Mat& src) {
	int row = src.rows;
	int col = src.cols;
	cv::Mat gray;
	cvtColor(src, gray, COLOR_BGR2GRAY);
	float m = mean(gray)[0];
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			if (gray.at<uchar>(i, j) > m) {
				// 背景
				dst.at<Vec3b>(i, j)[0] = 0;
				dst.at<Vec3b>(i, j)[1] = 0;
				dst.at<Vec3b>(i, j)[2] = 139;
			}
			else {
				// 前景
				dst.at<Vec3b>(i, j)[0] = 255;
				dst.at<Vec3b>(i, j)[1] = 255;
				dst.at<Vec3b>(i, j)[2] = 255;
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::Translate(cv::Mat src, int tx, int ty, bool flag) {
	int row = src.rows;
	int col = src.cols;
	if (flag) {//扩大
		Mat dst(row + abs(ty), col + abs(tx), CV_8UC3);
		int height = dst.rows;
		int width = dst.cols;
		for (int i = 0; i < height; i++) {
			int i0 = i - ty;
			if (i0 >= 0 && i0 < height) {
				for (int j = 0; j < width; j++) {
					int j0 = j - tx;
					if (j0 >= 0 && j0 < col && i0 >= 0 && i0 < row) {
						for (int k = 0; k < 3; k++) {
							dst.at<Vec3b>(i, j)[k] = src.at<Vec3b>(i0, j0)[k];
						}
					}
					else {
						for (int k = 0; k < 3; k++) {
							dst.at<Vec3b>(i, j)[k] = 255;
						}
					}
				}
			}
			else {
				for (int j = 0; j < width; j++) {
					for (int k = 0; k < 3; k++) {
						dst.at<Vec3b>(i, j)[k] = 255;
					}
				}
			}
		}
		return dst;
	}
	else {
		Mat dst(row, col, CV_8UC3);
		if (tx < -col || tx > col || ty < -row || ty > row) { //整个移出图像区域，则直接返回
			dst = Scalar::all(0);
			return dst;
		}
		int height = dst.rows;
		int width = dst.cols;
		for (int i = 0; i < height; i++) {
			int i0 = i - ty;
			if (i0 >= 0 && i0 < height) {
				for (int j = 0; j < width; j++) {
					int j0 = j - tx;
					if (j0 >= 0 && j0 < width && i0 >= 0 && i0 < height) {
						for (int k = 0; k < 3; k++) {
							dst.at<Vec3b>(i, j)[k] = src.at<Vec3b>(i0, j0)[k];
						}
					}
					else {
						for (int k = 0; k < 3; k++) {
							dst.at<Vec3b>(i, j)[k] = 255;
						}
					}
				}
			}
			else {
				for (int j = 0; j < width; j++) {
					for (int k = 0; k < 3; k++) {
						dst.at<Vec3b>(i, j)[k] = 255;
					}
				}
			}
		}
		return dst;
	}
}


cv::Mat PhotoShop::TranslatePtr(const cv::Mat& src, int tx, int ty, bool flag)
{
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst;

	if (flag) { // 扩大
		dst = Mat(row + abs(ty), col + abs(tx), CV_8UC3, Scalar(255, 255, 255));
		int height = dst.rows;
		int width = dst.cols;

		for (int i = 0; i < height; i++) {
			int i0 = i - ty;
			if (i0 >= 0 && i0 < row) {
				Vec3b* dst_row = dst.ptr<Vec3b>(i);
				const Vec3b* src_row = src.ptr<Vec3b>(i0);
				for (int j = 0; j < width; j++) {
					int j0 = j - tx;
					if (j0 >= 0 && j0 < col) {
						dst_row[j] = src_row[j0];
					}
				}
			}
		}
	}
	else {
		dst = Mat(row, col, CV_8UC3, Scalar(255, 255, 255));
		if (tx < -col || tx > col || ty < -row || ty > row) { // 整个移出图像区域，则直接返回
			return dst;
		}

		int height = dst.rows;
		int width = dst.cols;

		for (int i = 0; i < height; i++) {
			int i0 = i - ty;
			if (i0 >= 0 && i0 < row) {
				Vec3b* dst_row = dst.ptr<Vec3b>(i);
				const Vec3b* src_row = src.ptr<Vec3b>(i0);
				for (int j = 0; j < width; j++) {
					int j0 = j - tx;
					if (j0 >= 0 && j0 < col) {
						dst_row[j] = src_row[j0];
					}
				}
			}
		}
	}

	return dst;
}


cv::Mat PhotoShop::Sketch(cv::Mat src) {
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC1);
	cv::Mat gray(row, col, CV_8UC1);
	cvtColor(src, dst, COLOR_BGR2GRAY);
	gray = dst.clone();

	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			dst.at<uchar>(i, j) = 255 - dst.at<uchar>(i, j);
		}
	}
	GaussianBlur(dst, dst, Size(3, 3), 1.0);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			double b = double(dst.at<uchar>(i, j));
			double a = double(gray.at<uchar>(i, j));
			int temp = (int)(a + a * b / (256.0 - b));
			if (temp > 255)
				temp = 255;
			dst.at<uchar>(i, j) = temp;
		}
	}
	return dst;
}


cv::Mat PhotoShop::SketchPtr(const cv::Mat& src)
{
	
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC1);
	cv::Mat gray(row, col, CV_8UC1);
	cvtColor(src, dst, COLOR_BGR2GRAY);
	gray = dst.clone();

	bitwise_not(dst, dst);
	GaussianBlur(dst, dst, Size(3, 3), 1.0);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			double b = double(dst.at<uchar>(i, j));
			double a = double(gray.at<uchar>(i, j));
			int temp = (int)(a + a * b / (256.0 - b));
			if (temp > 255)
				temp = 255;
			dst.at<uchar>(i, j) = temp;
		}
	}
	return dst;
	
	/*
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC1);
	cv::Mat gray(row, col, CV_8UC1);

	cvtColor(src, gray, COLOR_BGR2GRAY);
	dst = gray.clone();
	bitwise_not(dst, dst);
	GaussianBlur(dst, dst, Size(3, 3), 1.0);

	// 合成素描效果
	for (int i = 0; i < row; i++) {
		uchar* dst_row = dst.ptr<uchar>(i);
		uchar* gray_row = gray.ptr<uchar>(i);
		for (int j = 0; j < col; j++) {
			double b = double(dst_row[j]);
			double a = double(gray_row[j]);
			int temp = (int)(a + a * b / (256.0 - b));
			if (temp > 255)
				temp = 255;
			dst_row[j] = temp;
		}
	}

	return dst;
	*/
}



cv::Mat PhotoShop::AddSubMulDiv(cv::Mat src1, cv::Mat src2, int flag) {
	int row = src1.rows;
	int col = src1.cols;

	if (row != src2.rows || col != src2.cols || src1.channels() != 3 || src2.channels() != 3) {
		fprintf(stderr, "Input shape don't match or src1/src2.channels != 3!");
		exit(-1);
	}

	Mat dst(row, col, CV_8UC3);

	auto processPixel = [&](Vec3b& src1Pixel, Vec3b& src2Pixel, Vec3b& dstPixel, int k) {
		int val;
		switch (flag) {
		case 0: // Add
			val = src1Pixel[k] + src2Pixel[k];
			dstPixel[k] = CLAMP(val);
			break;
		case 1: // Subtract
			val = src1Pixel[k] - src2Pixel[k];
			dstPixel[k] = CLAMP(val);
			break;
		case 2: // Multiply
			val = src1Pixel[k] * src2Pixel[k] / 255;
			dstPixel[k] = CLAMP(val);
			break;
		case 3: // Divide
			val = static_cast<int>((1.0 * src1Pixel[k] / (1.0 * src2Pixel[k] + 1.0)) * 255.0 + 0.5);
			dstPixel[k] = CLAMP(val);
			break;
		default:
			fprintf(stderr, "Invalid flag value!");
			exit(-1);
		}
	};

	for (int i = 0; i < row; i++) {
		Vec3b* src1Ptr = src1.ptr<Vec3b>(i);
		Vec3b* src2Ptr = src2.ptr<Vec3b>(i);
		Vec3b* dstPtr = dst.ptr<Vec3b>(i);

		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				processPixel(src1Ptr[j], src2Ptr[j], dstPtr[j], k);
			}
		}
	}

	return dst;
}



cv::Mat PhotoShop::FreeSharp(cv::Mat src, float sharpDegree)
{
	int row = src.rows;
	int col = src.cols;
	int border = 1;
	Mat dst(row, col, CV_8UC3);
	for (int i = border; i < row - border; i++) {
		for (int j = border; j < col - border; j++) {
			for (int k = 0; k < 3; k++) {
				int sum = 9 * src.at<Vec3b>(i, j)[k] - src.at<Vec3b>(i - 1, j - 1)[k] - src.at<Vec3b>(i - 1, j)[k]
					- src.at<Vec3b>(i - 1, j + 1)[k] - src.at<Vec3b>(i, j - 1)[k] - src.at<Vec3b>(i, j + 1)[k]
					- src.at<Vec3b>(i + 1, j - 1)[k] - src.at<Vec3b>(i + 1, j)[k] - src.at<Vec3b>(i + 1, j + 1)[k];
				sum = sum * sharpDegree + 0.5;
				if (sum > 255) sum = 255;
				else if (sum < 0) sum = 0;
				dst.at<Vec3b>(i, j)[k] = sum;
			}
		}
	}
	return dst;
}

cv::Mat PhotoShop::FreeSharpPtr(const cv::Mat& src, float sharpDegree)
{
	return cv::Mat();
}


cv::Mat PhotoShop::Equalizer(Mat src) {
	int row = src.rows;
	int col = src.cols;
	int Count[256] = { 0 };
	float p[256] = { 0 };
	float fSum[256] = { 0 };
	int level[256] = { 0 };
	int Total = row * col * 3;
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				Count[src.at<Vec3b>(i, j)[k]]++;
			}
		}
	}
	for (int i = 0; i < 256; i++) {
		p[i] = 1.0 * Count[i] / (1.0 * Total);
		if (i == 0)
			fSum[0] = p[0];
		else
			fSum[i] = fSum[i - 1] + p[i];
		level[i] = saturate_cast<uchar>(255 * fSum[i] + 0.5);
	}
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				dst.at<Vec3b>(i, j)[k] = level[src.at<Vec3b>(i, j)[k]];
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::EqualizerPtr(const cv::Mat& src)
{
	int row = src.rows;
	int col = src.cols;
	int Count[256] = { 0 }; // 对应坐标索引的像素值出现的频率
	float p[256] = { 0 };
	float fSum[256] = { 0 };
	int level[256] = { 0 };
	int Total = row * col * 3;

	// 计算像素值的频率
	for (int i = 0; i < row; i++) {
		const uchar* src_row = src.ptr<uchar>(i);
		for (int j = 0; j < col; j++) {
			Count[*src_row++]++;
			Count[*src_row++]++;
			Count[*src_row++]++;
		}
	}

	// 计算累积分布函数
	for (int i = 0; i < 256; i++) {
		p[i] = 1.0 * Count[i] / (1.0 * Total);
		if (i == 0)
			fSum[0] = p[0];
		else
			fSum[i] = fSum[i - 1] + p[i];
		level[i] = saturate_cast<uchar>(255 * fSum[i] + 0.5);
	}

	// 生成均衡化后的图像
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		const Vec3b* src_row = src.ptr<Vec3b>(i);
		Vec3b* dst_row = dst.ptr<Vec3b>(i);
		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				dst_row[j][k] = level[src_row[j][k]];
			}
		}
	}

	return dst;
}


cv::Mat PhotoShop::ColorBalance(cv::Mat src, int cR, int cG, int cB)
{
	int row = src.rows;
	int col = src.cols;
	//验证参数范围
	if (cR < -255)
		cR = -255;
	if (cR > 255)
		cR = 255;
	if (cG < -255)
		cG = -255;
	if (cG > 255)
		cG = 255;
	if (cB < -255)
		cB = -255;
	if (cB > 255)
		cB = 255;
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			int B = src.at<Vec3b>(i, j)[0] + cB;
			int G = src.at<Vec3b>(i, j)[1] + cG;
			int R = src.at<Vec3b>(i, j)[2] + cR;
			if (B < 0)
				B = 0;
			else if (B > 255)
				B = 255;
			if (G < 0)
				G = 0;
			else if (G > 255)
				G = 255;
			if (R < 0)
				R = 0;
			else if (R > 255)
				R = 255;
			dst.at<Vec3b>(i, j)[0] = B;
			dst.at<Vec3b>(i, j)[1] = G;
			dst.at<Vec3b>(i, j)[2] = R;
		}
	}
	return dst;
}


cv::Mat PhotoShop::ColorBalancePtr(const cv::Mat& src, int cR, int cG, int cB)
{
	int row = src.rows;
	int col = src.cols;

	// 验证参数范围
	cR = COLOR_255_255(cR);
	cG = COLOR_255_255(cG);
	cB = COLOR_255_255(cB);

	cv::Mat dst(row, col, CV_8UC3);

	for (int i = 0; i < row; i++) {
		const uchar* srcPtr = src.ptr<uchar>(i);
		uchar* dstPtr = dst.ptr<uchar>(i);

		for (int j = 0; j < col; j++) {
			*dstPtr++ = CLAMP(*srcPtr++ + cB);
			*dstPtr++ = CLAMP(*srcPtr++ + cG);
			*dstPtr++ = CLAMP(*srcPtr++ + cR);
		}
	}

	return dst;
}


cv::Mat PhotoShop::Equalize(cv::Mat src)
{
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC3);
	int HistGram[256] = { 0 };
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				HistGram[(int)src.at<Vec3b>(i, j)[k]]++;
			}
		}
	}
	int Num = 0;
	int LUT[256];
	for (int i = 0; i < 256; i++) {
		Num += HistGram[i];
		LUT[i] = (int)((float)Num / (row * col * 3) * 255);
	}
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				dst.at<Vec3b>(i, j)[k] = LUT[(int)src.at<Vec3b>(i, j)[k]];
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::EqualizePtr(const cv::Mat& src)
{

	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC3);
	int HistGram[256] = { 0 };

	// 计算直方图
	for (int i = 0; i < row; i++) {
		const uchar* srcPtr = src.ptr<uchar>(i);
		for (int j = 0; j < col; j++) {
			HistGram[*srcPtr++]++;
			HistGram[*srcPtr++]++;
			HistGram[*srcPtr++]++;
		}
	}

	// 计算累积分布函数
	int Num = 0;
	int LUT[256];
	for (int i = 0; i < 256; i++) {
		Num += HistGram[i];
		LUT[i] = static_cast<int>((float)Num / (row * col * 3) * 255);
	}

	// 应用查找表
	for (int i = 0; i < row; i++) {
		const uchar* srcPtr = src.ptr<uchar>(i);
		uchar* dstPtr = dst.ptr<uchar>(i);
		for (int j = 0; j < col; j++) {
			*dstPtr++ = LUT[*srcPtr++];
			*dstPtr++ = LUT[*srcPtr++];
			*dstPtr++ = LUT[*srcPtr++];
		}
	}

	return dst;
}



cv::Mat PhotoShop::LaplaceSharp(Mat src) {
	int row = src.rows;
	int col = src.cols;
	int border = 1;
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = border; i < row - border; i++) {
		for (int j = border; j < col - border; j++) {
			for (int k = 0; k < 3; k++) {
				int sum = 9 * src.at<Vec3b>(i, j)[k] - src.at<Vec3b>(i - 1, j - 1)[k] - src.at<Vec3b>(i - 1, j)[k]
					- src.at<Vec3b>(i - 1, j + 1)[k] - src.at<Vec3b>(i, j - 1)[k] - src.at<Vec3b>(i, j + 1)[k]
					- src.at<Vec3b>(i + 1, j - 1)[k] - src.at<Vec3b>(i + 1, j)[k] - src.at<Vec3b>(i + 1, j + 1)[k];
				if (sum > 255) sum = 255;
				else if (sum < 0) sum = 0;
				dst.at<Vec3b>(i, j)[k] = sum;
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::LaplaceSharpPtr(const cv::Mat& src)
{
	int row = src.rows;
	int col = src.cols;
	int border = 1;
	cv::Mat dst(row, col, CV_8UC3);

	for (int i = border; i < row - border; i++) {
		Vec3b* dst_row = dst.ptr<Vec3b>(i);
		const Vec3b* src_row = src.ptr<Vec3b>(i);
		const Vec3b* src_row_prev = src.ptr<Vec3b>(i - 1);
		const Vec3b* src_row_next = src.ptr<Vec3b>(i + 1);

		for (int j = border; j < col - border; j++) {
			for (int k = 0; k < 3; k++) {
				int sum = 9 * src_row[j][k]
					- src_row_prev[j - 1][k] - src_row_prev[j][k] - src_row_prev[j + 1][k]
					- src_row[j - 1][k] - src_row[j + 1][k]
					- src_row_next[j - 1][k] - src_row_next[j][k] - src_row_next[j + 1][k];
				dst_row[j][k] = CLAMP(sum);
			}
		}
	}

	return dst;
}


cv::Mat PhotoShop::Pince(Mat src, int degree) {
	int row = src.rows;
	int col = src.cols;
	if (degree < 1) degree = 1;
	if (degree > 32) degree = 32;
	cv::Mat dst(row, col, CV_8UC3);
	//图片的中心点
	int midX = col / 2;
	int midY = row / 2;
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			int offsetX = j - midX;
			int offsetY = i - midY;
			double radian = atan2((double)offsetY, (double)offsetX);
			double radius = sqrtf((double)(offsetX * offsetX + offsetY * offsetY));
			//实现挤压
			radius = sqrtf(radius) * degree;
			int X = (int)(radius * cos(radian)) + midX;
			int Y = (int)(radius * sin(radian)) + midY;
			if (X < 0) X = 0;
			if (X >= col) X = col - 1;
			if (Y < 0) Y = 0;
			if (Y >= row) Y = row - 1;
			for (int k = 0; k < 3; k++) {
				dst.at<Vec3b>(i, j)[k] = src.at<Vec3b>(Y, X)[k];
			}
		}
	}
	return dst;
}



cv::Mat PhotoShop::Emboss(Mat src, float angle, int offset) 
{
	const float PI = acos(-1.0);
	int row = src.rows;
	int col = src.cols;
	float radian = angle * PI / 180.0;
	//float kernel[] =   // 灰色浮雕模板系数
	//{
	//	cos(radian + PI / 4), cos(radian + PI / 2), cos(radian + 3.0*PI / 4.0),
	//	cos(radian),        0,                  cos(PI),
	//	cos(radian - PI / 2), cos(radian - PI / 2), cos(radian - 3.0*PI / 4.0)
	//};
	float kernel[] =   // 彩色浮雕模板系数
	{
		cos(radian + PI / 4), cos(radian + PI / 2), cos(radian + 3.0 * PI / 4.0),
		cos(radian), 1, cos(PI),
		cos(radian - PI / 2), cos(radian - PI / 2), cos(radian - 3.0 * PI / 4.0)
	};
	cv::Mat dst(row, col, CV_8UC1);
	int border = 1;
	for (int i = border; i < row - border; i++) {
		for (int j = border; j < col - border; j++) {
			float sum = 0;
			int id = 0;
			for (int x = -border; x <= border; x++) {
				for (int y = -border; y <= border; y++) {
					sum = sum + float(1.0 * src.at<uchar>(i + x, j + y) * kernel[id++]);
				}
			}
			if (sum > 255) sum = 255;
			else if (sum < 0) sum = 0;
			dst.at<uchar>(i, j) = sum;
		}
	}
	return dst;
}


cv::Mat PhotoShop::EmbossPtr(const cv::Mat& src, float angle, int offset)
{
	const float PI = acos(-1.0);
	int row = src.rows;
	int col = src.cols;
	float radian = angle * PI / 180.0;

	// 彩色浮雕模板系数
	float kernel[] = {
		cos(radian + PI / 4), cos(radian + PI / 2), cos(radian + 3.0 * PI / 4.0),
		cos(radian), 1, cos(PI),
		cos(radian - PI / 2), cos(radian - PI / 2), cos(radian - 3.0 * PI / 4.0)
	};

	cv::Mat dst(row, col, CV_8UC1);
	int border = 1;
	for (int i = border; i < row - border; i++) {
		uchar* dst_row = dst.ptr<uchar>(i);
		for (int j = border; j < col - border; j++) {
			float sum = 0;
			int id = 0;
			for (int x = -border; x <= border; x++) {
				const uchar* src_row = src.ptr<uchar>(i + x);
				for (int y = -border; y <= border; y++) {
					sum += float(src_row[j + y]) * kernel[id++];
				}
			}
			dst_row[j] = CLAMP(static_cast<int>(sum));
		}
	}
	return dst;
}


cv::Mat PhotoShop::MirrorTransposition(const cv::Mat& src, int flag)
{
	/*
	============== 使用opencv自带函数 ===========
	// 水平翻转
	Mat horizontalFlip;
	flip(src, horizontalFlip, 1);

	// 垂直翻转
	Mat verticalFlip;
	flip(src, verticalFlip, 0);

	// 水平和垂直同时翻转
	Mat bothFlip;
	flip(src, bothFlip, -1);

	// 转置图像
	Mat transposed;
	transpose(src, transposed);
	* 
	*/
	int row = src.rows;
	int col = src.cols;
	Mat dst(row, col, CV_8UC3);
	if (flag == 0) {
		for (int i = 0; i < row; i++) {
			const Vec3b* srcPtr = src.ptr<Vec3b>(i);
			for (int j = 0; j < col; j++) {
				Vec3b* dstPtr = dst.ptr<Vec3b>(j);
				dstPtr[i] = srcPtr[j];
			}
		}
	}
	else if (flag == 1) {
		for (int i = 0; i < row; i++) {
			const Vec3b* srcPtr = src.ptr<Vec3b>(i);
			Vec3b* dstPtr = dst.ptr<Vec3b>(i);
			for (int j = 0; j < col; j++) {
				dstPtr[j] = srcPtr[col - 1 - j];
			}
		}
	}
	else {
		for (int i = 0; i < row; i++) {
			const Vec3b* srcPtr = src.ptr<Vec3b>(row - 1 - i);
			Vec3b* dstPtr = dst.ptr<Vec3b>(i);
			for (int j = 0; j < col; j++) {
				dstPtr[j] = srcPtr[j];
			}
		}
	}

	return dst;
}


cv::Mat PhotoShop::Slant(const cv::Mat& src, float angle, int flag)
{
	const double PI = 3.1415926;
	int rows = src.rows;
	int cols = src.cols;
	float ftan = fabs((float)tan(angle / 180.0 * PI));
	int newHeight = 0;
	int newWidth = 0;
	if (flag == 1) {  //水平方向高度不变
		newHeight = rows;
		newWidth = (int)(cols + rows * fabs(ftan));
	}
	else {            //垂直方向宽度不变
		newHeight = (int)(rows + cols * fabs(ftan));
		newWidth = cols;
	}
	Mat dst(rows, cols, CV_8UC3);
	for (int i = 0; i < newHeight; i++) {
		for (int j = 0; j < newWidth; j++) {
			int newi, newj;
			if (flag == 1) {
				newi = i;
				newj = j + ftan * (i - rows);
			}
			else {
				newi = i + ftan * (j - cols);
				newj = j;
			}
			if (newi >= 0 && newi < rows && newj >= 0 && newj < cols) {
				for (int k = 0; k < 3; k++) {
					dst.at<Vec3b>(i, j)[k] = src.at<Vec3b>(newi, newj)[k];
				}
			}
			else {
				for (int k = 0; k < 3; k++) {
					dst.at<Vec3b>(i, j)[k] = 255;
				}
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::SlantPtr(const cv::Mat& src, float angle, int flag)
{
	const double PI = 3.1415926;
	int rows = src.rows;
	int cols = src.cols;
	float ftan = fabs((float)tan(angle / 180.0 * PI));
	int newHeight = 0;
	int newWidth = 0;

	if (flag == 1) {  // 水平方向高度不变
		newHeight = rows;
		newWidth = static_cast<int>(cols + rows * fabs(ftan));
	}
	else {            // 垂直方向宽度不变
		newHeight = static_cast<int>(rows + cols * fabs(ftan));
		newWidth = cols;
	}

	/* 设置默认背景色（255，255，255）*/
	cv::Mat dst(newHeight, newWidth, CV_8UC3, Scalar(255, 255, 255));

	for (int i = 0; i < newHeight; i++) {
		Vec3b* dstPtr = dst.ptr<Vec3b>(i);
		for (int j = 0; j < newWidth; j++) {
			int newi, newj;
			if (flag == 1) {
				newi = i;
				newj = static_cast<int>(j + ftan * (i - rows));
			}
			else {
				newi = static_cast<int>(i + ftan * (j - cols));
				newj = j;
			}
			if (newi >= 0 && newi < rows && newj >= 0 && newj < cols) {
				const Vec3b* srcPtr = src.ptr<Vec3b>(newi);
				dstPtr[j] = srcPtr[newj];
			}
		}
	}

	return dst;
}



cv::Mat PhotoShop::Contrast(Mat src, int degree) {
	int row = src.rows;
	int col = src.cols;
	//验证参数范围
	if (degree < -100) degree = -100;
	if (degree > 100) degree = 100;
	double contrast = (100.0 + degree) / 100.0;
	contrast *= contrast;
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				int val = (int)(((1.0 * src.at<Vec3b>(i, j)[k] / 255.0 - 0.5) * contrast + 0.5) * 255);
				if (val < 0) val = 0;
				else if (val > 255) val = 255;
				dst.at<Vec3b>(i, j)[k] = val;
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::ContrastPtr(const cv::Mat& src, int degree)
{
	int row = src.rows;
	int col = src.cols;

	// 验证参数范围
	degree = DEGREE_100_100(degree);
	double contrast = (100.0 + degree) / 100.0;
	contrast *= contrast;

	cv::Mat dst(row, col, CV_8UC3);

	for (int i = 0; i < row; i++) {
		const uchar* srcPtr = src.ptr<uchar>(i);
		uchar* dstPtr = dst.ptr<uchar>(i);
		for (int j = 0; j < col; j++) {
			*dstPtr++ = CLAMP(static_cast<int>(((*srcPtr++ / 255.0 - 0.5) * contrast + 0.5) * 255));
			*dstPtr++ = CLAMP(static_cast<int>(((*srcPtr++ / 255.0 - 0.5) * contrast + 0.5) * 255));
			*dstPtr++ = CLAMP(static_cast<int>(((*srcPtr++ / 255.0 - 0.5) * contrast + 0.5) * 255));
		}
	}

	return dst;
}



cv::Mat PhotoShop::Harmonic(cv::Mat src, int offset) {
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC3);
	int border = 1;
	for (int i = border; i < row - border; i++) {
		for (int j = border; j < col - border; j++) {
			for (int k = 0; k < 3; k++) {
				int sum = 4 * src.at<Vec3b>(i, j)[k]
					- src.at<Vec3b>(i - 1, j - 1)[k]
					- src.at<Vec3b>(i - 1, j + 1)[k]
					- src.at<Vec3b>(i + 1, j - 1)[k]
					- src.at<Vec3b>(i + 1, j + 1)[k] + offset;
				if (sum > 255) sum = 255;
				else if (sum < 0) sum = 0;
				dst.at<Vec3b>(i, j)[k] = sum;
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::HarmonicPtr(const cv::Mat& src, int offset) 
{
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC3);
	int border = 1;

	for (int i = border; i < row - border; i++) {
		Vec3b* dst_row = dst.ptr<Vec3b>(i);
		const Vec3b* src_row = src.ptr<Vec3b>(i);
		const Vec3b* src_row_prev = src.ptr<Vec3b>(i - 1);
		const Vec3b* src_row_next = src.ptr<Vec3b>(i + 1);

		for (int j = border; j < col - border; j++) {
			for (int k = 0; k < 3; k++) {
				int sum = 4 * src_row[j][k]
					- src_row_prev[j - 1][k]
					- src_row_prev[j + 1][k]
					- src_row_next[j - 1][k]
					- src_row_next[j + 1][k] + offset;
				dst_row[j][k] = CLAMP(sum);
			}
		}
	}

	return dst;
}


cv::Mat PhotoShop::Invert(cv::Mat src) {
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				dst.at<Vec3b>(i, j)[k] = 255 ^ src.at<Vec3b>(i, j)[k];
			}
		}
	}
	return dst;
}



cv::Mat PhotoShop::InvertPtr(const cv::Mat& src) 
{
	if (src.channels() != 3)
	{
		fprintf_s(stderr, "Input channels must 3!");
		exit(-1);
	}
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC3);

	for (int i = 0; i < row; i++) {
		uchar* dst_row = dst.ptr<uchar>(i);
		const uchar* src_row = src.ptr<uchar>(i);
		for (int j = 0; j < col; j++) {
			*dst_row++ = 255 ^ *src_row++;
			*dst_row++ = 255 ^ *src_row++;
			*dst_row++ = 255 ^ *src_row++;
		}
	}
	return dst;
}


cv::Mat PhotoShop::ComicStrips(cv::Mat src) {
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			int b = src.at<Vec3b>(i, j)[0];
			int g = src.at<Vec3b>(i, j)[1];
			int r = src.at<Vec3b>(i, j)[2];
			int R = abs(g - b + g + r) * r / 256;
			int G = abs(b - g + b + r) * r / 256;
			int B = abs(b - g + b + r) * g / 256;
			if (R > 255) R = 255;
			if (R < 0) R = 0;
			if (G > 255) G = 255;
			if (G < 0) G = 0;
			if (B > 255) B = 255;
			if (B < 0) B = 0;
			dst.at<Vec3b>(i, j)[0] = B;
			dst.at<Vec3b>(i, j)[1] = G;
			dst.at<Vec3b>(i, j)[2] = R;
		}
	}
	cv::Mat gray;
	cvtColor(dst, gray, COLOR_BGR2GRAY);
	return gray;
}



cv::Mat PhotoShop::UnsharpMask(cv::Mat src, int degree) {
	int row = src.rows;
	int col = src.cols;
	if (degree < 1) degree = 1;
	if (degree > 100) degree = 100;
	cv::Mat dst(row, col, CV_8UC3);
	src.copyTo(src);
	int border = 1;
	for (int i = 0; i < degree; i++) {
		GaussianBlur(dst, dst, Size(3, 3), 1.0);
	}
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				int sum = 2 * src.at<Vec3b>(i, j)[k] - dst.at<Vec3b>(i, j)[k];
				if (sum > 255) sum = 255;
				else if (sum < 0) sum = 0;
				dst.at<Vec3b>(i, j)[k] = sum;
			}
		}
	}
	return dst;
}



cv::Mat PhotoShop::Masic(cv::Mat src, int Ksize) {
	int offset = (Ksize - 1) / 2;
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = offset; i < row - offset; i += 2 * offset) {
		for (int j = offset; j < col - offset; j += 2 * offset) {
			int b = src.at<Vec3b>(i, j)[0];
			int g = src.at<Vec3b>(i, j)[1];
			int r = src.at<Vec3b>(i, j)[2];
			for (int x = -offset; x < offset; x++) {
				for (int y = -offset; y < offset; y++) {
					dst.at<Vec3b>(i + x, j + y)[0] = b;
					dst.at<Vec3b>(i + x, j + y)[1] = g;
					dst.at<Vec3b>(i + x, j + y)[2] = r;
				}
			}
		}
	}
	return dst;
}



cv::Mat PhotoShop::MasicPtr(const cv::Mat& src, int Ksize) {
	int offset = (Ksize - 1) / 2;
	int row = src.rows;
	int col = src.cols;
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = offset; i < row - offset; i += 2 * offset) {
		const Vec3b* src_row = src.ptr<Vec3b>(i);
		for (int j = offset; j < col - offset; j += 2 * offset) {
			Vec3b pixel = src_row[j];
			for (int x = -offset; x < offset; x++) {
				Vec3b* dst_row = dst.ptr<Vec3b>(i + x);
				for (int y = -offset; y < offset; y++) {
					dst_row[j + y] = pixel;
				}
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::HighPass(cv::Mat src) {
	int row = src.rows;
	int col = src.cols;
	cv::Mat gau(row, col, CV_8UC3);
	GaussianBlur(src, gau, Size(3, 3), 0, 0);
	cv::Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				dst.at<Vec3b>(i, j)[k] = src.at<Vec3b>(i, j)[k] - gau.at<Vec3b>(i, j)[k] + 127;
				if (dst.at<Vec3b>(i, j)[k] > 255)
					dst.at<Vec3b>(i, j)[k] = 255;
				else if (dst.at<Vec3b>(i, j)[k] < 0)
					dst.at<Vec3b>(i, j)[k] = 0;
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::HighPassPtr(const cv::Mat& src) {
	int row = src.rows;
	int col = src.cols;
	cv::Mat gau(row, col, CV_8UC3);
	GaussianBlur(src, gau, Size(3, 3), 0, 0);
	cv::Mat dst(row, col, CV_8UC3);

	for (int i = 0; i < row; i++) {
		const uchar* src_row = src.ptr<uchar>(i);
		uchar* gau_row = gau.ptr<uchar>(i);
		uchar* dst_row = dst.ptr<uchar>(i);
		for (int j = 0; j < col; j++) {
			*dst_row++ = CLAMP(*src_row++ - *gau_row++ + 127);
			*dst_row++ = CLAMP(*src_row++ - *gau_row++ + 127);
			*dst_row++ = CLAMP(*src_row++ - *gau_row++ + 127);
		}
	}

	return dst;
}


cv::Mat PhotoShop::MedianFilter(cv::Mat src, int ksize)
{
	int row = src.rows;
	int col = src.cols;
	int border = (ksize - 1) / 2;
	int mid = (ksize * ksize - 1) / 2;
	Mat dst(row, col, CV_8UC3);
	for (int i = border; i < row - border; i++) {
		for (int j = border; j < col - border; j++) {
			for (int k = 0; k < 3; k++) {
			    std::vector <int> v;
				for (int x = -border; x <= border; x++) {
					for (int y = -border; y <= border; y++) {
						v.push_back(src.at<Vec3b>(i + x, j + y)[k]);
					}
				}
				sort(v.begin(), v.end());
				dst.at<Vec3b>(i, j)[k] = v[mid];
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::MedianFilterPtr(const cv::Mat& src, int ksize)
{
	int row = src.rows;
	int col = src.cols;
	int border = (ksize - 1) / 2;
	Mat dst(row, col, CV_8UC3);

	// 提前计算vector的大小并预先分配空间
	int vecSize = ksize * ksize;
	int mid = (vecSize - 1) / 2;
	std::vector<int> v(vecSize);

	// 使用指针遍历图像数据
	for (int i = border; i < row - border; i++) {
		Vec3b* dstPtr = dst.ptr<Vec3b>(i);
		for (int j = border; j < col - border; j++) {
			for (int k = 0; k < 3; k++) {
				int idx = 0;
				for (int x = -border; x <= border; x++) {
					for (int y = -border; y <= border; y++) {
						v[idx++] = src.at<Vec3b>(i + x, j + y)[k];
					}
				}
				std::sort(v.begin(), v.end());
				dstPtr[j][k] = static_cast<uchar>(v[mid]);
			}
		}
	}

	return dst;
}


cv::Mat PhotoShop::TemplateBlur(cv::Mat src)
{
	int row = src.rows;
	int col = src.cols;
	Mat dst(row, col, CV_8UC3);
	int border = 1;
	for (int i = border; i < row - border; i++) {
		for (int j = border; j < col - border; j++) {
			for (int k = 0; k < 3; k++) {
				int sum = src.at<Vec3b>(i, j)[k] + src.at<Vec3b>(i + 1, j)[k] + src.at<Vec3b>(i - 1, j)[k] +
					src.at<Vec3b>(i, j - 1)[k] + src.at<Vec3b>(i + 1, j - 1)[k] + src.at<Vec3b>(i - 1, j - 1)[k] +
					src.at<Vec3b>(i, j + 1)[k] + src.at<Vec3b>(i + 1, j + 1)[k] + src.at<Vec3b>(i - 1, j + 1)[k];
				sum /= 9;
				dst.at<Vec3b>(i, j)[k] = sum;
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::TemplateBlurPtr(const cv::Mat& src)
{
	int row = src.rows;
	int col = src.cols;
	Mat dst(row, col, CV_8UC3);
	int border = 1;

	for (int i = border; i < row - border; i++) {
		const Vec3b* srcPtrP = src.ptr<Vec3b>(i - 1);
		const Vec3b* srcPtrM = src.ptr<Vec3b>(i);
		const Vec3b* srcPtrN = src.ptr<Vec3b>(i + 1);
		Vec3b* dstPtr = dst.ptr<Vec3b>(i);
		for (int j = border; j < col - border; j++) {
			for (int k = 0; k < 3; k++) {
				int sum = srcPtrP[j - 1][k] + srcPtrP[j][k] + srcPtrP[j + 1][k]
					+ srcPtrM[j - 1][k] + srcPtrM[j][k] + srcPtrM[j + 1][k]
					+ srcPtrN[j - 1][k] + srcPtrN[j][k] + srcPtrN[j + 1][k];
				sum /= 9;
				dstPtr[j][k] = static_cast<uchar>(sum);
			}
		}
	}

	return dst;
}


cv::Mat PhotoShop::RadialBlurRotate(cv::Mat src, int num)
{
	int row = src.rows;
	int col = src.cols;
	Point center(row / 2, col / 2);
	Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			int R = norm(Point(i, j) - center);
			float angle = atan2(float(i - center.x), float(j - center.y));
			int sum1 = 0, sum2 = 0, sum3 = 0;
			for (int k = 0; k < num; k++) {
				angle += 0.01;
				int newX = R * sin(angle) + center.x;
				int newY = R * cos(angle) + center.y;
				if (newX < 0) newX = 0;
				if (newX > row - 1) newX = row - 1;
				if (newY < 0) newY = 0;
				if (newY > col - 1) newY = col - 1;
				sum1 += src.at<Vec3b>(newX, newY)[0];
				sum2 += src.at<Vec3b>(newX, newY)[1];
				sum3 += src.at<Vec3b>(newX, newY)[2];
			}
			dst.at<Vec3b>(i, j)[0] = (uchar)(sum1 / num);
			dst.at<Vec3b>(i, j)[1] = (uchar)(sum2 / num);
			dst.at<Vec3b>(i, j)[2] = (uchar)(sum3 / num);
		}
	}
	return dst;
}


cv::Mat PhotoShop::RadialBlurRotatePtr(const cv::Mat& src, int num)
{
	int row = src.rows;
	int col = src.cols;
	Point center(row / 2, col / 2);
	Mat dst(row, col, CV_8UC3);

	// 使用指针遍历图像数据
	for (int i = 0; i < row; i++) {
		Vec3b* dstPtr = dst.ptr<Vec3b>(i);
		for (int j = 0; j < col; j++) {
			int R = norm(Point(i, j) - center);
			float angle = atan2(float(i - center.x), float(j - center.y));
			int sum1 = 0, sum2 = 0, sum3 = 0;
			for (int k = 0; k < num; k++) {
				angle += 0.01;
				int newX = R * sin(angle) + center.x;
				int newY = R * cos(angle) + center.y;
				if (newX < 0) newX = 0;
				if (newX > row - 1) newX = row - 1;
				if (newY < 0) newY = 0;
				if (newY > col - 1) newY = col - 1;
				const Vec3b* srcPixel = src.ptr<Vec3b>(newX) + newY;
				sum1 += srcPixel->val[0];
				sum2 += srcPixel->val[1];
				sum3 += srcPixel->val[2];
			}
			dstPtr[j][0] = static_cast<uchar>(sum1 / num);
			dstPtr[j][1] = static_cast<uchar>(sum2 / num);
			dstPtr[j][2] = static_cast<uchar>(sum3 / num);
		}
	}

	return dst;
}


cv::Mat PhotoShop::RadialBlurZoom(cv::Mat src, int num)
{
	int row = src.rows;
	int col = src.cols;
	Point center(row / 2, col / 2);
	Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			int R = norm(Point(i, j) - center);
			float angle = atan2(float(i - center.x), float(j - center.y));
			int sum1 = 0, sum2 = 0, sum3 = 0;
			for (int k = 0; k < num; k++) {
				int tmpR = (R - k) > 0 ? (R - k) : 0;
				int newX = tmpR * sin(angle) + center.x;
				int newY = tmpR * cos(angle) + center.y;
				if (newX < 0) newX = 0;
				if (newX > row - 1) newX = row - 1;
				if (newY < 0) newY = 0;
				if (newY > col - 1) newY = col - 1;
				sum1 += src.at<Vec3b>(newX, newY)[0];
				sum2 += src.at<Vec3b>(newX, newY)[1];
				sum3 += src.at<Vec3b>(newX, newY)[2];
			}
			dst.at<Vec3b>(i, j)[0] = (uchar)(sum1 / num);
			dst.at<Vec3b>(i, j)[1] = (uchar)(sum2 / num);
			dst.at<Vec3b>(i, j)[2] = (uchar)(sum3 / num);
		}
	}
	return dst;
}


cv::Mat PhotoShop::RadialBlurZoomPtr(const cv::Mat& src, int num)
{
	int row = src.rows;
	int col = src.cols;
	Point center(row / 2, col / 2);
	Mat dst(row, col, CV_8UC3);

	// 使用指针遍历图像数据
	for (int i = 0; i < row; i++) {
		Vec3b* dstPtr = dst.ptr<Vec3b>(i);
		for (int j = 0; j < col; j++) {
			int R = norm(Point(i, j) - center);
			float angle = atan2(float(i - center.x), float(j - center.y));
			int sum1 = 0, sum2 = 0, sum3 = 0;
			for (int k = 0; k < num; k++) {
				int tmpR = (R - k) > 0 ? (R - k) : 0;
				int newX = tmpR * sin(angle) + center.x;
				int newY = tmpR * cos(angle) + center.y;
				if (newX < 0) newX = 0;
				if (newX > row - 1) newX = row - 1;
				if (newY < 0) newY = 0;
				if (newY > col - 1) newY = col - 1;
				const Vec3b* srcPixel = src.ptr<Vec3b>(newX) + newY;
				sum1 += srcPixel->val[0];
				sum2 += srcPixel->val[1];
				sum3 += srcPixel->val[2];
			}
			dstPtr[j][0] = static_cast<uchar>(sum1 / num);
			dstPtr[j][1] = static_cast<uchar>(sum2 / num);
			dstPtr[j][2] = static_cast<uchar>(sum3 / num);
		}
	}

	return dst;
}


cv::Mat PhotoShop::WaveFilter(cv::Mat src)
{
	const double pi = acos(-1.0);
	int row = src.rows;
	int col = src.cols;
	float A = 7;
	float B = 2.5;
	Point Center(col / 2, row / 2);
	Mat dst(row, col, CV_8UC3);
	for (int y = 0; y < row; y++) {
		for (int x = 0; x < col; x++) {
			float y0 = Center.y - y;
			float x0 = x - Center.x;
			float theta = atan(y0 / (x0 + 0.00001));
			if (x0 < 0)
				theta = theta + pi;
			float r0 = sqrt(x0 * x0 + y0 * y0);
			float r1 = r0 + A * col * 0.01 * sin(B * 0.1 * r0);

			float new_x = r1 * cos(theta);
			float new_y = r1 * sin(theta);

			new_x = Center.x + new_x;
			new_y = Center.y + new_y;

			if (new_x < 0) new_x = 0;
			if (new_x >= col - 1) new_x = col - 2;
			if (new_y < 0) new_y = 0;
			if (new_y >= row - 1) new_y = row - 2;
			int x1 = (int)new_x;
			int y1 = (int)new_y;
			float p = new_x - x1;
			float q = new_y - y1;
			for (int k = 0; k < 3; k++) {
				dst.at<Vec3b>(y, x)[k] = (1 - p) * (1 - q) * src.at<Vec3b>(y1, x1)[k] + (p) * (1 - q) * src.at<Vec3b>(y1, x1 + 1)[k] +
					(1 - p) * (q)*src.at<Vec3b>(y1 + 1, x1)[k] + (p) * (q)*src.at<Vec3b>(y1 + 1, x1 + 1)[k];
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::ColorClotPtr(const cv::Mat& src, int radius)
{
	int row = src.rows;
	int col = src.cols;
	int border = (radius - 1) / 2;
	Mat dst(row, col, CV_8UC3);

	// 使用指针遍历图像数据
	for (int i = border; i + border < row; i++) {
		Vec3b* dstPtr = dst.ptr<Vec3b>(i);
		for (int j = border; j + border < col; j++) {
			for (int k = 0; k < 3; k++) {
				int val = src.at<Vec3b>(i, j)[k];
				for (int x = -border; x <= border; x++) {
					for (int y = -border; y <= border; y++) {
						val = std::min(val, (int)src.at<Vec3b>(i + x, j + y)[k]);
					}
				}
				dstPtr[j][k] = static_cast<uchar>(val);
			}
		}
	}

	return dst;
}


cv::Mat PhotoShop::ColorClot(cv::Mat src, int radius)
{
	int row = src.rows;
	int col = src.cols;
	int border = (radius - 1) / 2;
	Mat dst(row, col, CV_8UC3);
	for (int i = border; i + border < row; i++) {
		for (int j = border; j + border < col; j++) {
			for (int k = 0; k < 3; k++) {
				int val = src.at<Vec3b>(i, j)[k];
				for (int x = -border; x <= border; x++) {
					for (int y = -border; y <= border; y++) {
						val = min(val, (int)src.at<Vec3b>(i + x, j + y)[k]);
					}
				}
				dst.at<Vec3b>(i, j)[k] = val;
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::SurfaceBlur(cv::Mat src, int radius)
{
	int row = src.rows;
	int col = src.cols;
	Mat dst(row, col, CV_8UC3);
	double Thres = 25;
	int border = (radius - 1) / 2;
	for (int k = 0; k < 3; k++) {
		for (int i = border; i + border < row; i++) {
			for (int j = border; j + border < col; j++) {
				double sum1 = 0;
				double sum2 = 0;
				for (int x = -border; x <= border; x++) {
					for (int y = -border; y <= border; y++) {
						double w = 1.0 - (abs(src.at<Vec3b>(i + x, j + y)[k] - src.at<Vec3b>(i, j)[k]) / (2.5 * Thres));
						if (w < 0) w = 0;
						sum1 += w * src.at<Vec3b>(i + x, j + y)[k];
						sum2 += w;
					}
				}
				dst.at<Vec3b>(i, j)[k] = sum1 / sum2;
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::SurfaceBlurPtr(cv::Mat src, int radius)
{
	int row = src.rows;
	int col = src.cols;
	Mat dst(row, col, CV_8UC3);
	double Thres = 25;
	int border = (radius - 1) / 2;

	for (int k = 0; k < 3; k++) {
		for (int i = border; i + border < row; i++) {
			for (int j = border; j + border < col; j++) {
				double sum1 = 0;
				double sum2 = 0;
				uchar center_value = src.at<Vec3b>(i, j)[k];

				for (int x = -border; x <= border; x++) {
					for (int y = -border; y <= border; y++) {
						uchar value = src.at<Vec3b>(i + x, j + y)[k];
						double w = 1.0 - (abs(value - center_value) / (2.5 * Thres));
						if (w > 0) {
							sum1 += w * value;
							sum2 += w;
						}
					}
				}
				dst.at<Vec3b>(i, j)[k] = static_cast<uchar>(sum1 / sum2);
			}
		}
	}

	return dst;
}


cv::Mat PhotoShop::MotionBlur(const cv::Mat& src, int angle, int distance)
{
	if (distance < 1) distance = 1;
	else if (distance > 200) distance = 200;
	double radian = ((double)angle + 180.0) / 180.0 * 3.1415926f;
	int dx = (int)((double)distance * cos(radian) + 0.5);
	int dy = (int)((double)distance * sin(radian) + 0.5);
	int sign;
	if (dx < 0) sign = -1;
	if (dx > 0) sign = 1;
	int height = src.rows;
	int width = src.cols;
	int chns = src.channels();
	Mat dst;
	dst.create(height, width, src.type());
	for (int i = 0; i < height; i++) {
		unsigned  char* dstData = (unsigned char*)dst.data + dst.step * i;
		for (int j = 0; j < width; j++) {
			for (int k = 0; k < chns; k++) {
				int sum = 0, count = 0;
				for (int p = 0; p < abs(dx); p++) {
					int i0 = i + p * sign;
					int j0 = j + p * sign;
					if (i0 >= 0 && i0 < height && j0 >= 0 && j0 < width) {
						count++;
						sum += src.at<Vec3b>(i0, j0)[k];
					}
				}
				if (count == 0) {
					dstData[j * chns + k] = src.at<Vec3b>(i, j)[k];
				}
				else {
					dstData[j * chns + k] = int(sum / (double)count + 0.5);
					if (dstData[j * chns + k] < 0) dstData[j * chns + k] = 0;
					else if (dstData[j * chns + k] > 255) dstData[j * chns + k] = 255;
				}
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::MotionBlurPtr(const cv::Mat& src, int angle, int distance)
{
	if (distance < 1) distance = 1;
	else if (distance > 200) distance = 200;
	double radian = ((double)angle + 180.0) / 180.0 * 3.1415926f;
	int dx = (int)((double)distance * cos(radian) + 0.5);
	int dy = (int)((double)distance * sin(radian) + 0.5);
	int sign = (dx < 0) ? -1 : 1;
	int height = src.rows;
	int width = src.cols;
	int chns = src.channels();
	Mat dst(height, width, src.type());

	// 使用指针遍历图像数据
	for (int i = 0; i < height; i++) {
		Vec3b* dstPtr = dst.ptr<Vec3b>(i);
		for (int j = 0; j < width; j++) {
			for (int k = 0; k < chns; k++) {
				int sum = 0, count = 0;
				for (int p = 0; p < abs(dx); p++) {
					int i0 = i + p * sign;
					int j0 = j + p * sign;
					if (i0 >= 0 && i0 < height && j0 >= 0 && j0 < width) {
						count++;
						sum += src.at<Vec3b>(i0, j0)[k];
					}
				}
				if (count == 0) {
					dstPtr[j][k] = src.at<Vec3b>(i, j)[k];
				}
				else {
					dstPtr[j][k] = CLAMP(sum / count);
				}
			}
		}
	}

	return dst;
}



const float EPS = 1e-7;
void CreatGaussKernel(float** pdKernel, int kSize, float sigma) {
	int sum = 0;
	float dCenter = (kSize - 1) / 2;
	//生成高斯数据
	for (int i = 0; i < kSize; i++) {
		for (int j = 0; j < kSize; j++) {
			
			//用和来近似平方和的开方
			float dis = fabsf(i - dCenter) + fabsf(j - dCenter);
			float val = exp(-dis * dis / (2 * sigma * sigma + EPS));
			pdKernel[i][j] = val;
			sum += val;
		}
	}
	//归一化
	for (int i = 0; i < kSize; i++) {
		for (int j = 0; j < kSize; j++) {
			pdKernel[i][j] /= (sum + EPS);
		}
	}
}


cv::Mat PhotoShop::GaussBlur(cv::Mat src, int kSize, float sigma)
{
	int row = src.rows;
	int col = src.cols;
	//分配高斯核空间
	float** pKernel = new float* [kSize];
	for (int i = 0; i < kSize; i++) {
		pKernel[i] = new float[kSize];
	}
	Mat dst(row, col, CV_8UC3);
	CreatGaussKernel(pKernel, kSize, sigma);
	int border = (kSize - 1) / 2;
	float sum = 0;
	for (int i = border; i < row - border; i++) {
		for (int j = border; j < col - border; j++) {
			for (int k = 0; k < 3; k++) {
				sum = 0;
				for (int x = -border; x <= border; x++) {
					for (int y = -border; y <= border; y++) {
						sum += src.at<Vec3b>(i + x, j + y)[k] * pKernel[border + x][border + y];
					}
				}
				if (sum > 255) sum = 255;
				else if (sum < 0) sum = 0;
				dst.at<Vec3b>(i, j)[k] = sum;
			}
		}
	}
	return dst;
}


cv::Mat PhotoShop::GaussBlurPtr(cv::Mat src, int kSize, float sigma)
{
	int row = src.rows;
	int col = src.cols;

	// 分配高斯核空间
	float** pKernel = new float* [kSize];
	for (int i = 0; i < kSize; i++) {
		pKernel[i] = new float[kSize];
	}
	Mat dst(row, col, CV_8UC3);
	CreatGaussKernel(pKernel, kSize, sigma);
	int border = (kSize - 1) / 2;

	for (int i = border; i < row - border; i++) {
		for (int j = border; j < col - border; j++) {
			for (int k = 0; k < 3; k++) {
				float sum = 0;
				for (int x = -border; x <= border; x++) {
					for (int y = -border; y <= border; y++) {
						sum += src.at<Vec3b>(i + x, j + y)[k] * pKernel[border + x][border + y];
					}
				}
				dst.at<Vec3b>(i, j)[k] = CLAMP(sum);
			}
		}
	}

	// 释放高斯核空间
	for (int i = 0; i < kSize; i++) {
		delete[] pKernel[i];
	}
	delete[] pKernel;
	return dst;
}

